package com.jzo2o.market.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.CommonUserApi;
import com.jzo2o.api.customer.dto.response.CommonUserResDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.enums.CouponTypeEnum;
import com.jzo2o.market.mapper.CouponMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.CouponPageQueryDTO;
import com.jzo2o.market.model.dto.response.CouponPageInfoResDTO;
import com.jzo2o.market.model.dto.response.CouponSimpleInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.mysql.utils.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 * @author itcast
 * @since 2023-09-16
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
    @Resource
    private CommonUserApi commonUserApi;
    @Resource
    private IActivityService activityService;

    @Override
    public PageResult<CouponPageInfoResDTO> page(CouponPageQueryDTO couponPageQueryDTO) {
        if (ObjectUtils.isEmpty(couponPageQueryDTO.getActivityId())) {
            return new PageResult<>(0L, 0L, new ArrayList<>());
        }

        Page<Coupon> couponPage = PageUtils.parsePageQuery(couponPageQueryDTO, Coupon.class);
        LambdaQueryWrapper<Coupon> queryWrapper = Wrappers.<Coupon>lambdaQuery()
                .eq(Coupon::getActivityId, couponPageQueryDTO.getActivityId());

        return PageUtils.toPage(baseMapper.selectPage(couponPage, queryWrapper), CouponPageInfoResDTO.class);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<CouponSimpleInfoResDTO> getCurrentUserCoupon(CouponStatusEnum status, Integer lastId) {
        Long userId = UserContext.currentUserId();
        if (ObjectUtils.isEmpty(userId)) {
            throw new BadRequestException("用户未授权");
        }

        List<Coupon> coupons = lambdaQuery()
                .eq(Coupon::getUserId, userId)
                .eq(Coupon::getStatus, status)
                .lt(ObjectUtils.isNotEmpty(lastId), Coupon::getId, lastId)
                .orderByDesc(Coupon::getCreateTime)
                .last("LIMIT 10")
                .list();

        return CollUtils.isEmpty(coupons) ? new ArrayList<>() :
                coupons.stream()
                        .map(coupon -> BeanUtils.toBean(coupon, CouponSimpleInfoResDTO.class))
                        .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void invalidExpiredCoupon() {
        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE)
                // 不处理还没到有效期的优惠卷
                .le(Coupon::getValidityTime, LocalDateTime.now())
                .select(Coupon::getId)
                .list();

        if (CollUtils.isEmpty(couponList)) {
            return;
        }

        // 设置状态已失效
        couponList = couponList.stream()
                .map(coupon -> coupon.setStatus(CouponStatusEnum.INVALID))
                .collect(Collectors.toList());

        if (!this.updateBatchById(couponList)) {
            throw new DBException("更新优惠卷状态失败");
        }
    }

    @Override
    @Transactional
    public void syncCouponRecord(long activityId, long userId) {
        Activity activity = activityService.getById(activityId);
        CommonUserResDTO user = commonUserApi.findById(userId);

        if (!this.save(Coupon.builder()
                .userId(userId)
                .userName(user.getNickname())
                .userPhone(user.getPhone())
                .activityId(activityId)
                .name(activity.getName())
                .type(activity.getType())
                .amountCondition(activity.getAmountCondition())
                .discountRate(activity.getDiscountRate())
                .discountAmount(activity.getDiscountAmount())
                .validityTime(LocalDateTime.now().plusDays(activity.getValidityDays()))
                .status(CouponStatusEnum.NO_USE)
                .build())) {
            throw new DBException("插入优惠卷表失败");
        }

        if (!activityService.lambdaUpdate()
                .setSql("stock_num = stock_num - 1")
                .eq(Activity::getId, activityId)
                .gt(Activity::getStockNum, 0)
                .update()) {
            throw new DBException("更新活动库存失败");
        }
    }

    @Override
    public List<AvailableCouponsResDTO> getAvailableCoupon(BigDecimal totalAmount) {
        Long userId = Optional
                .ofNullable(UserContext.currentUserId())
                .orElseThrow(() -> new CommonException("用户信息不存在"));

        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getUserId, userId)
                .le(Coupon::getAmountCondition, totalAmount)
                .eq(Coupon::getStatus, CouponStatusEnum.NO_USE)
                .ge(Coupon::getValidityTime, LocalDateTime.now())
                .and(and -> and
                        .isNull(Coupon::getDiscountAmount)
                        .or(or -> or.le(Coupon::getDiscountAmount, totalAmount)))
                .list();

        if (CollUtils.isEmpty(couponList)) {
            return new ArrayList<>();
        }

        return couponList.stream()
                .map(coupon -> BeanUtils
                        .toBean(coupon, AvailableCouponsResDTO.class)
                        .setType(coupon.getType().getType())
                        .setDiscountAmount(this.calcDiscountAmount(coupon, totalAmount)))
                // 默认BigDecimal的比较器排序从小到大 需要按优惠金额从大到小排序
                .sorted(Comparator.comparing(AvailableCouponsResDTO::getDiscountAmount).reversed())
                .collect(Collectors.toList());
    }

    /**
     * 计算优惠卷的优惠价格
     * @param coupon      优惠卷信息
     * @param totalAmount 订单总金额
     */
    @Override
    public BigDecimal calcDiscountAmount(Coupon coupon, BigDecimal totalAmount) {
        CouponTypeEnum type = coupon.getType();
        BigDecimal discountAmount = BigDecimal.ZERO;

        if (type == CouponTypeEnum.AMOUNT_DISCOUNT) {
            discountAmount = coupon.getDiscountAmount();
        } else if (type == CouponTypeEnum.RATE_DISCOUNT) {
            // 1 <= coupon.getDiscountRate() <= 99
            BigDecimal discountRate = new BigDecimal(String.format("0.%02d", 100 - coupon.getDiscountRate()));
            discountAmount = totalAmount.multiply(discountRate);
        }

        return discountAmount;
    }
}